﻿using DetectScars.model;
using ScottPlot;
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ZstdNet;

namespace DetectScars.common
{
    /** C扫过程中的A扫数据处理工具类
     * 
     */
    class AcquisitionData
    {
        public static int[,][] DATA_A;
        public static int ROW;
        public static int COL;
        public static bool enable = false;
        public static long z=0;
        public static double encoder_z = 0;
        public static double encoder_x = 0;
        public static float J = 0;
        public static int M = 0;
        public static int N = 0;
        public static string path_dir = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "data/temp");
        //c扫当前下标
        public static int indexC_x = 0;
        public static int indexC_y = 0;
        //d扫下标
        public static int indexD_x = 0;
        public static int indexD_y = 0;
        //每次下降的d的行数
        public static int PER_D = 0;
        public static List<Defect> list_data;
        public static List<CError> list_error;
        // 探头 xyz轴的初始位置
        public static float plc_x = 0;
        public static float plc_y = 0;
        public static float plc_z = 0;
        //闸门相关
        public static float gata_start = 0;
        public static float gata_end = 0;
        public static float gata_height = 0;
        //绘制图形相关
        //c扫高度
        public static float height_c = 0;
        //c扫宽度
        public static float weight_c = 0;
        //a扫宽度起始
        public static float weight_a_start = 0;
        //a扫宽度结束
        public static float weight_a_end = 0;
        //初始化数据
        public static void InitData(int height, int weight, double encoder_z_, double encoder_x_,float J_,int rows,int cols,int pre_d, double weight_c_, double height_c_, double weight_a_start_, double weight_a_end_)
        {
            DATA_A = new int[height, weight][];
            encoder_z = encoder_z_;
            encoder_x = encoder_x_;
            J = J_;
            M = height;
            N=weight;
            z = 0;
            ROW = rows; 
            COL = cols;
            indexC_x = 0;
            indexC_y = 0;
            indexD_x = 0;
            indexD_y = 0;
            PER_D = pre_d;
            list_data = new List<Defect>();
            list_error = new List<CError>();
            //plc_x = PlcControlUtils.getCurrectPosition("X");
            //plc_y = PlcControlUtils.getCurrectPosition("Y");
            //plc_z = PlcControlUtils.getCurrectPosition("Z");
            height_c = (float)height_c_;
            weight_c = (float)weight_c_;
            weight_a_start = (float)weight_a_start_;
            weight_a_end = (float)weight_a_end_;
        }
        //开启或关闭临时数据搜集
        public static void setEnable(bool enable_)
        {
            enable = enable_;  
        }
        //添加到数据集
        public static void addDataA(long z_,long x, int[] data_a)
        {
            if (!enable)
            {
                return;
            }
            if (z==0)
            {
                z = z_;
                indexC_x = 0;
            }
            else
            {
                indexC_x = (int)((z_ - z) / encoder_z+0.5);
            }
            if (indexC_x >= M|| indexC_x < 0)
            {
                indexC_x = 0;
                return;
            }
            indexC_y = (int)(x / encoder_x);
            if (indexC_y >= N || indexC_y < 0)
            {
                return;
            }
            DATA_A[indexC_x, indexC_y] = data_a;
        }
        //添加缺陷数据
        public static void addListDate(Defect qx)
        {
            list_data.Add(qx);
        }
        public static void addTestDataA(int[] data_a)
        {
            if(z == M*N)
            {
                z++;
                MessageBox.Show("搜集完毕");
                saveData();
                return;
            }
            else if (z>M*N)
            {
                return;
            }
            DATA_A[z/N, z%N] = data_a;
            z++;

        }
        //保存临时数据
        public static void saveData()
        {
            string filename = path_dir+"temp.bin";
            if (!Directory.Exists(path_dir))
            {
                Directory.CreateDirectory(path_dir);
            }
            SaveToZstd(filename,DATA_A);
        }
        //打开临时数据
        public static void readData()
        {
            string filename = path_dir + "temp.bin";
            if (!Directory.Exists(path_dir))
            {
                Directory.CreateDirectory(path_dir);
            }
            DATA_A = LoadFromZstd(filename);
        }
        //从临时数据获取


        //更新d扫数据
        public static void updateDataD(double[,] dataY)
        {
            if (!enable)
            {
                return;
            }
            if (indexC_y>=N || indexC_y < 0)
            {
                return;
            }
            if (indexD_x == indexC_x)
            {
                for (int j = indexD_y; j < indexC_y + 1; j++)
                {
                    if (j>= DATA_A.GetLength(1))
                    {
                        continue;
                    }
                    setDataD(dataY, DATA_A[indexD_x, j], indexD_x* PER_D, ROW);
                }
            }
            else
            {
                for (int i = indexD_x; i < indexC_x + 1; i++)
                {
                    if ( i == indexC_x)
                    {
                        for (int j = 0; j < indexC_y + 1; j++)
                        {
                            setDataD(dataY, DATA_A[i, j], i * PER_D, ROW);
                        }
                    }
                    else
                    {
                        for (int j = indexD_y; j < DATA_A.GetLength(1); j++)
                        {
                            setDataD(dataY, DATA_A[i, j], i * PER_D, ROW);
                        }
                    }
                }
            }
            
            indexD_x = indexC_x;
            indexD_y = indexC_y;
        }



        static void setDataD(double[,] dataY, int[] dataY2,int index_x,int len)
        {
            if (dataY2 == null)
            {
                return;
            }
            for(int i = 0; i < len; i++)
            {
                if (index_x + i >= dataY.GetLength(0))
                {
                    continue;
                }
                for (int j = 0;j < dataY.GetLength(1); j++)
                {
                    if (dataY[index_x + i, j]< dataY2[i* dataY.GetLength(1)+j])
                    {
                        dataY[index_x + i,j] = dataY2[i * dataY.GetLength(1) + j];
                    }
                }
            }

        }

        //更新c扫与d扫数据
        public static void updateDataCAndD(double[,] dataC, double[,] dataD)
        {
            for(int i = 0; i < DATA_A.GetLength(0); i++)
            {
                for(int j=0;j< DATA_A.GetLength(1); j++)
                {
                    int[] data = DATA_A[i, j];
                    if (data == null || data.Length<ROW*COL)
                    {
                        continue;
                    }
                    for (int k = 0; k < ROW; k++)
                    {
                        //int temp = 0;
                        for (int l = 0; l < COL; l++)
                        {
                            if (i * (ROW - 1) + k>= dataD.GetLength(0))
                            {
                                continue;
                            }
                            if (dataD[i*(ROW-1)+k,l]< data[k*COL+l])
                            {
                                dataD[i * (ROW - 1) + k, l] = data[k * COL + l];
                            }
                            //if (l>0&&l<1)
                            //{
                            //    if (temp< data[k * COL + l])
                            //    {
                            //        temp = data[k * COL + l];
                            //    }
                            //}
                        }
                        //dataC[i * (ROW - 1) + k, j] = temp;
                    }
                }
            }
        }

        static void SaveToZstd(string path, int[,][] ragged2D)
        {
            int M = ragged2D.GetLength(0);
            int N = ragged2D.GetLength(1);
            var indexList = new List<(int offset, int length)>();
            var dataBytes = new List<byte>();

            for (int i = 0; i < M; i++)
            {
                for (int j = 0; j < N; j++)
                {
                    int[] row = ragged2D[i, j];
                    int offset = dataBytes.Count;
                    if (row == null)
                    {
                        continue;
                    }
                    foreach (var val in row)
                    {
                        int tp = (int) (val / 16);
                        if (tp < 0 || tp > 255)
                            throw new Exception($"值超出范围: {val}");
                        dataBytes.Add((byte)tp); // bit-level 8-bit 打包
                    }

                    int length = row.Length;
                    indexList.Add((offset, length));
                }
            }

            using (var ms = new MemoryStream())
            using (var bw = new BinaryWriter(ms))
            {
                // Header
                bw.Write(M);
                bw.Write(N);

                // Index table
                foreach (var (offset, length) in indexList)
                {
                    bw.Write(offset);
                    bw.Write(length);
                }

                // Data section (already 8-bit packed)
                bw.Write(dataBytes.ToArray());

                // 压缩
                byte[] uncompressed = ms.ToArray();
                using var compressor = new Compressor();
                byte[] compressed = compressor.Wrap(uncompressed);

                File.WriteAllBytes(path, compressed);
            }
        }

        static int[,][] LoadFromZstd(string path)
        {
            byte[] compressed = File.ReadAllBytes(path);
            using var decompressor = new Decompressor();
            byte[] uncompressed = decompressor.Unwrap(compressed);

            using var ms = new MemoryStream(uncompressed);
            using var br = new BinaryReader(ms);

            int M = br.ReadInt32();
            int N = br.ReadInt32();
            int total = M * N;

            int[] offsets = new int[total];
            int[] lengths = new int[total];
            for (int i = 0; i < total; i++)
            {
                offsets[i] = br.ReadInt32();
                lengths[i] = br.ReadInt32();
            }

            byte[] dataBlock = br.ReadBytes(uncompressed.Length - (int)ms.Position);
            var result = new int[M, N][];

            for (int i = 0; i < total; i++)
            {
                int offset = offsets[i];
                int length = lengths[i];
                if (i>1&&length != lengths[i-1])
                {
                    return result;
                }
                int[] arr = new int[length];
                for (int j = 0; j < length; j++)
                {
                    if (offset + j>= dataBlock.Length)
                    {
                        continue;
                    }
                    // 解 8-bit packed 值
                    arr[j] = dataBlock[offset + j] * 16;
                }
                     

                int row = i / N;
                int col = i % N;
                result[row, col] = arr;
            }

            return result;
        }

        //序列化数据到文件中
        public static void Serialization(string filepath,string type)
        {
            if (DATA_A == null)
            {
                MessageBox.Show("未找到可保存的数据");
                return;
            }
            Dictionary<string, string> dict_data = new Dictionary<string, string>();
            //多少mm激发一次a扫
            dict_data.Add("J", J.ToString());
            //a扫数组的行
            dict_data.Add("ROW", ROW.ToString());
            //a扫数组的列
            dict_data.Add("COL", COL.ToString());
            //z轴编码器的转换值
            dict_data.Add("encoder_z", encoder_z.ToString());
            //旋转轴编码器的转换值
            dict_data.Add("encoder_x", encoder_x.ToString());
            //c扫数组的行
            dict_data.Add("M", M.ToString());
            //c扫数组的列
            dict_data.Add("N", N.ToString());
            //z轴每次下降对应c扫的行数
            dict_data.Add("PER_D", PER_D.ToString());
            //缺陷数据
            dict_data.Add("list_data", Newtonsoft.Json.JsonConvert.SerializeObject(list_data));
            //x
            dict_data.Add("plc_x", plc_x.ToString());
            //y
            dict_data.Add("plc_y", plc_y.ToString());
            //z
            dict_data.Add("plc_z", plc_z.ToString());
            dict_data.Add("gata_height", gata_height.ToString());
            dict_data.Add("weight_c", weight_c.ToString());
            dict_data.Add("height_c", height_c.ToString());
            dict_data.Add("weight_a_start", weight_a_start.ToString());
            dict_data.Add("weight_a_end", weight_a_end.ToString());
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(dict_data);
            //更新文件
            System.IO.File.WriteAllText(filepath, json);
            if (type == "new")
            {

                string dataApath = filepath.Substring(0, filepath.Length - 6) + ".bin";
                //更新a扫data
                SaveToZstd(dataApath, DATA_A);
                //更新c扫数据
                string dataCpath = filepath.Substring(0, filepath.Length - 6) + ".txt";
                //FileUtils.SaveDataToFile(Main.flatData_C, dataCpath);
            }
            
        }
        //反序列化文件中的数据
        public static void Deserialization(string filepath)
        {
            //反序列化index文件
            string file_index = filepath.Substring(0, filepath.Length - 4) + ".index";
            if(!File.Exists(file_index))
            {
                MessageBox.Show("未找到与该数据对应的配置文件");
                return;
            }
            string file_datac = filepath.Substring(0, filepath.Length - 4) + ".txt";
            if (!File.Exists(file_index))
            {
                MessageBox.Show("未找到c扫数据");
                return;
            }
            string json = File.ReadAllText(file_index);
            Dictionary<string, string>? dict_data = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
            if (dict_data == null)
            {
                MessageBox.Show("获取配置文件数据异常");
                return;
            }
            ROW = int.Parse(dict_data["ROW"]);
            COL = int.Parse(dict_data["COL"]);
            enable = false;
            z = 0;
            encoder_z = double.Parse(dict_data["encoder_z"]);
            encoder_x = double.Parse(dict_data["encoder_x"]);
            J = float.Parse(dict_data["J"]);
            M = int.Parse(dict_data["M"]);
            N = int.Parse(dict_data["N"]);
            indexC_x = 0;
            indexC_y = 0;
            indexD_x = 0;
            indexD_y = 0;
            plc_x = float.Parse(dict_data["plc_x"]);
            plc_y = float.Parse(dict_data["plc_y"]);
            plc_z = float.Parse(dict_data["plc_z"]);
            PER_D = int.Parse(dict_data["PER_D"]);
            weight_c = float.Parse(dict_data["weight_c"]);
            height_c = float.Parse(dict_data["height_c"]);
            weight_a_start = float.Parse(dict_data["weight_a_start"]);
            weight_a_end = float.Parse(dict_data["weight_a_end"]);
            gata_height = float.Parse(dict_data["gata_height"]);
            list_data = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Defect>>(dict_data["list_data"]);
            //反序列bin文件
            DATA_A = LoadFromZstd(filepath);
            if (DATA_A == null)
            {
                MessageBox.Show("加载数据异常");
                return;
            }
            //Main.flatData_C = FileUtils.LoadDataFromFile(file_datac);
           

        }


        public static DataTable GetDefects(float Z_PRE_MM,decimal sd)
        {
            if (list_data == null || gata_height == 0)
            {
                return null;
            }
            //height_c = 416;
            //gata_height = 1024;
            DataTable dt = new DataTable();
            dt.TableName = "defect";
            dt.Columns.Add("VerticalDistance", Type.GetType("System.Decimal"));
            dt.Columns.Add("LateralDistance", Type.GetType("System.Decimal"));
            dt.Columns.Add("SoundInterval", Type.GetType("System.Decimal"));
            dt.Columns.Add("SignalValue", Type.GetType("System.Decimal"));
            foreach (Defect defect in list_data)
            {
                dt.Rows.Add(height_c- 50 - defect.VerticalDistance * Z_PRE_MM,
                    defect.LateralDistance * J, defect.SoundInterval / 2000 * (float)sd,
                    (20 * Math.Log10(defect.SignalValue / gata_height)).ToString("0.00")); ;
            }

            return dt;
        }
    }
}
